{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "# Code to show how different corner modes work on extreme geometry\n",
    "\n",
    "The purpose of this is to show off upgrades the GDSFactory built-in code to allow users to access the different corner modes offered by klayout. This has proved useful to me for fixing DRC minimum space and minimum width issues in real tapeouts where I had geometry that was more effectively cut by the 'octagon_limit' than the 'square_limit' that is the current default (and only option in the main branch as of the writing of this notebook)\n",
    "\n",
    "Author: Eli Doris, Oct 17 2025"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "import gdsfactory as gf\n",
    "from gdsfactory.typings import CornerMode\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Start with some extreme geometry that would cause major DRC problems\n",
    "def extreme_shape():\n",
    "    c = gf.Component()\n",
    "    triangle = gf.c.triangle4(x=1,y=12,spacing=0,layer=(64,0))\n",
    "    angles = np.arange(0,180,180/9)\n",
    "    for angle in angles:\n",
    "        t = c << triangle\n",
    "        t.rotate(angle)\n",
    "        t.x = 0\n",
    "        t.y = 0\n",
    "    c.flatten()\n",
    "    return c\n",
    "\n",
    "shape_base = extreme_shape()\n",
    "shape_base.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Show behavior under different modes for positive offset\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    c = gf.Component()\n",
    "    shape_base = c << extreme_shape()\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.offset(layer=(64,0),distance=1,corner_mode=corner_mode)\n",
    "    shape_offset.remap_layers({\n",
    "        (64,0) : (10,0)\n",
    "    })\n",
    "    c << shape_offset\n",
    "    c.plot()\n",
    "    plt.title('Positive Offset (Expand/Dilate) Mode: ' + mode.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Show behavior for negative offset \n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    c = gf.Component()\n",
    "    shape_base = c << extreme_shape()\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.offset(layer=(64,0),distance=-0.4,corner_mode=corner_mode)\n",
    "    shape_offset.remap_layers({\n",
    "        (64,0) : (12,0)\n",
    "    })\n",
    "    c << shape_offset\n",
    "    c.plot()\n",
    "    plt.title('Negative Offset (Contract/Erode) Mode: ' + mode.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Show behavior for minimum space fixing mode (positive then negative)\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    c = gf.Component()\n",
    "    shape_base = c << extreme_shape()\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.offset(layer=(64,0),distance=0.5,corner_mode=corner_mode)\n",
    "    shape_offset.offset(layer=(64,0),distance=-0.5,corner_mode=corner_mode)\n",
    "    shape_offset.remap_layers({\n",
    "        (64,0) : (25,0)\n",
    "    })\n",
    "    c << shape_offset\n",
    "    c.plot()\n",
    "    plt.title('Fix Space (positive then negative offset) Mode: ' + mode.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Show behavior for minimum width fixing mode (negative then positive)\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    c = gf.Component()\n",
    "    shape_base = c << extreme_shape()\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.offset(layer=(64,0),distance=-0.5,corner_mode=corner_mode)\n",
    "    shape_offset.offset(layer=(64,0),distance=0.5,corner_mode=corner_mode)\n",
    "    shape_offset.remap_layers({\n",
    "        (64,0) : (31,0)\n",
    "    })\n",
    "    c << shape_offset\n",
    "    c.plot()\n",
    "    plt.title('Fix Width (negative then positive offset) Mode: ' + mode.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot all modes on top of one another, positive offset\n",
    "c = gf.Component()\n",
    "base_shape = extreme_shape()\n",
    "base_shape.remap_layers({\n",
    "    (64,0) : (10,0)\n",
    "})\n",
    "c << base_shape\n",
    "\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.offset(layer=(64,0),distance=1,corner_mode=corner_mode)\n",
    "    c << shape_offset\n",
    "    \n",
    "c.plot()\n",
    "plt.title('Positive Offset (Expand/Dilate) All Modes')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot all modes on top of one another, negative offset\n",
    "c = gf.Component()\n",
    "base_shape = extreme_shape()\n",
    "base_shape.remap_layers({\n",
    "    (64,0) : (12,0)\n",
    "})\n",
    "c << base_shape\n",
    "\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.offset(layer=(64,0),distance=-0.4,corner_mode=corner_mode)\n",
    "    c << shape_offset\n",
    "    \n",
    "c.plot()\n",
    "plt.title('Negative Offset (Contract/Erode) All Modes')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot all modes on top of one another, fix minimum spacing mode\n",
    "c = gf.Component()\n",
    "base_shape = extreme_shape()\n",
    "base_shape.remap_layers({\n",
    "    (64,0) : (25,0)\n",
    "})\n",
    "c << base_shape\n",
    "\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.over_under(layer=(64,0),distance=0.5,corner_mode=corner_mode)\n",
    "    c << shape_offset\n",
    "    \n",
    "c.plot()\n",
    "plt.title('Fix Minimum Spacing (Positive then Negative) All Modes')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot all modes on top of one another, fix minimum width mode\n",
    "c = gf.Component()\n",
    "# # Too difficult to see with the base shape present\n",
    "# base_shape = extreme_shape()\n",
    "# base_shape.remap_layers({\n",
    "#     (64,0) : (102,0)\n",
    "# })\n",
    "# c << base_shape\n",
    "\n",
    "for mode in CornerMode:\n",
    "    corner_mode = mode.value\n",
    "    shape_offset = extreme_shape()\n",
    "    shape_offset.over_under(layer=(64,0),distance=-0.5,corner_mode=corner_mode)\n",
    "    c << shape_offset\n",
    "    \n",
    "c.plot()\n",
    "plt.title('Fix Minimum Width (Negative then Positive) All Modes')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Example of what this might look like for DRC fixing minimum width and space simultaneously\n",
    "# Use minimum width: 0.2, minimum spacing: 0.4, angle checking where octagon_limit is appropriate\n",
    "\n",
    "c = gf.Component()\n",
    "drc_mode = CornerMode.octagon_limit\n",
    "shape_offset = extreme_shape()\n",
    "shape_offset.over_under(layer=(64,0),distance=0.4,corner_mode=drc_mode)\n",
    "shape_offset.over_under(layer=(64,0),distance=-0.2,corner_mode=drc_mode)\n",
    "c << shape_offset\n",
    "    \n",
    "c.plot()\n",
    "plt.title('Fix Minimum Width and Minimum Spacing, Octagon Limit Mode')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "gfdev",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
